package org.ktm.servlet;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.ktm.core.KTMContext;
import org.ktm.utils.Globals;
import org.ktm.utils.Localizer;
import org.ktm.utils.PropertyUtils;
import org.ktm.web.bean.FormBean;

public abstract class AbstractServlet extends HttpServlet {
	private static final long			serialVersionUID	= 1L;

	public static final String			POST_METHOD			= "htmlPost";
	public static final String			GET_METHOD			= "htmlGet";
	public static final String			DEFAULT_MODULE		= "form";

	private KTMContext					appContext;
	private final Map<String,FieldType>	paramMap			= new HashMap<String,FieldType>();
	private final Vector<String>		paramKey			= new Vector<String>();

	public AbstractServlet() {
		super();
	}

	protected abstract ActionForward
			processRequest( FormBean form,
							HttpServletRequest request,
							HttpServletResponse response,
							final String htmlMethod,
							final String htmlModule )	throws ServletException,
														IOException;

	public abstract String getBeanClass();

	protected ServletContext getServletContext( HttpServletRequest request ) {
		return request.getSession().getServletContext();
	}

	public String getCookieValue(	HttpServletRequest request,
									String cookieName,
									String defaultVal ) {
		Cookie [] cookies = request.getCookies();
		if ( cookies != null ) {
			for ( int i = 0; i < cookies.length; i++ ) {
				Cookie cookie = cookies[ i ];
				if ( cookieName.equals( cookie.getName() ) ) return ( cookie.getValue() );
			}
		}
		return ( defaultVal );
	}

	public Cookie setCookieValue(	HttpServletResponse response,
									String cookieName,
									String value ) {
		Cookie cookie = new Cookie( cookieName, value );
		response.addCookie( cookie );
		return cookie;
	}

	public	FormBean
			getBean( HttpServletRequest request )	throws ClassNotFoundException,
													InstantiationException,
													IllegalAccessException {
		HttpSession session = request.getSession();
		FormBean bean = (FormBean) session.getAttribute( getBeanClass() );
		if ( bean == null ) {
			Class<?> c = null;

			ClassLoader classLoader = Thread.currentThread()
					.getContextClassLoader();

			if ( classLoader != null ) {
				c = classLoader.loadClass( getBeanClass() );
			} else {
				c = Class.forName( getBeanClass() );
			}
			if ( c != null ) {
				bean = (FormBean) c.newInstance();
				bean.setServlet( this );
				session.setAttribute( getBeanClass(), bean );
			}
		}
		return bean;
	}

	private Object getFieldTypeObject( FieldType type, String value ) {
		Object result = null;
		if ( type.getValue() != null && type.getValue() instanceof Integer ) {
			result = Integer.parseInt( value == null ? "0" : value );
		} else if ( type.getValue() != null && type.getValue() instanceof Double ) {
			result = Double.parseDouble( value == null ? "0" : value );
		} else if ( type.getValue() != null && type.getValue() instanceof Long ) {
			result = Long.parseLong( value == null ? "0" : value );
		} else if ( type.getValue() != null && type.getValue() instanceof Boolean ) {
			result = Boolean.parseBoolean( value == null ? "0" : value );
		} else if ( type.getValue() != null && type.getValue() instanceof Short ) {
			result = Short.parseShort( value == null ? "0" : value );
		} // else if() {} // TODO: more type support
		else {
			result = value == null ? "" : String.valueOf( value );
		}
		return result;
	}

	private void performLoadParameter(	Map<String,FieldType> paramMap,
										HttpServletRequest request,
										FormBean bean ) {
		Iterator<String> it = paramKey.iterator();
		while ( it.hasNext() ) {
			String paramName = it.next();
			FieldType type = paramMap.get( paramName );
			String svalue = request.getParameter( paramName );
			Object obj = getFieldTypeObject( type, svalue );
			PropertyUtils.setProperty( bean, type.getFieldName(), obj );
		}
	}

	private void performAddFieldName(	FormBean bean,
										String prefix,
										Class<?> clazz ) {
		Field [] fields = clazz.getDeclaredFields();
		for ( Field field : fields ) {
			String fieldName = field.getName();
			Object obj = PropertyUtils.getProperty( bean, fieldName );
			if ( obj instanceof FormBean ) {
				performAddFieldName( (FormBean) obj, fieldName, obj.getClass() );
			} else {
				if ( !prefix.isEmpty() ) {
					fieldName = prefix + "." + fieldName;
				}
				if ( !paramMap.containsKey( fieldName ) ) {
					paramMap.put( fieldName, new FieldType( fieldName, obj ) );
					paramKey.add( fieldName );
				}
			}
		}

		if ( !Localizer.getClassName( clazz.getSuperclass() ).equals( "Object" ) ) {
			performAddFieldName( bean, prefix, clazz.getSuperclass() );
		}
	}

	private Map<String,FieldType>
			collectAllParamName( HttpServletRequest request, FormBean bean ) {
		paramMap.clear();
		paramKey.clear();

		performAddFieldName( bean, "", bean.getClass() );

		return paramMap;
	}

	protected boolean
			prepareRequest( HttpServletRequest request ) throws ServletException,
														IOException {
		ServletContext ctx = getServletContext( request );
		appContext = (KTMContext) ctx.getAttribute( KTMContext.APPCONTEXT );

		try {
			FormBean bean = getBean( request );
			bean.reset();
			performLoadParameter( collectAllParamName( request, bean ),
					request,
					bean );
		}
		catch ( ClassNotFoundException e ) {
			e.printStackTrace();
		}
		catch ( InstantiationException e ) {
			e.printStackTrace();
		}
		catch ( IllegalAccessException e ) {
			e.printStackTrace();
		}
		return true;
	}

	protected void
			postRequest(	HttpServletRequest request,
							HttpServletResponse response,
							ActionForward action )	throws ServletException,
													IOException {
		try {
			request.setAttribute( "bean", getBean( request ) );
		}
		catch ( ClassNotFoundException e ) {
			e.printStackTrace();
		}
		catch ( InstantiationException e ) {
			e.printStackTrace();
		}
		catch ( IllegalAccessException e ) {
			e.printStackTrace();
		}
		if ( action != null ) {
			String uri = action.getForwardUri();
			while ( uri.indexOf( "//" ) > 0 ) {
				uri = uri.replaceFirst( "//", "/" );
			}
			if ( action.isRedirect() ) {
				response.sendRedirect( uri );
			} else {
				request.getRequestDispatcher( uri ).forward( request, response );
			}

			if ( action.isEndConversation() ) {
				closeSession( request );
			}
		}
	}

	protected ActionForward
			processRequest( HttpServletRequest request,
							HttpServletResponse response,
							final String htmlMethod,
							final String htmlModule )	throws ServletException,
														IOException,
														ClassNotFoundException,
														InstantiationException,
														IllegalAccessException {
		String mainUri = getServletContext( request ).getInitParameter( Globals.MAIN_PAGE );
		ActionForward action = ActionForward.getUri( this, request, mainUri );
		if ( prepareRequest( request ) ) {
			action = processRequest( getBean( request ),
					request,
					response,
					htmlMethod,
					htmlModule );
		}
		return action;
	}

	@Override
	protected void
			doGet( HttpServletRequest request, HttpServletResponse response )	throws ServletException,
																				IOException {
		try {
			ActionForward action = processRequest( request,
					response,
					GET_METHOD,
					DEFAULT_MODULE );
			postRequest( request, response, action );
		}
		catch ( ClassNotFoundException e ) {
			e.printStackTrace();
		}
		catch ( InstantiationException e ) {
			e.printStackTrace();
		}
		catch ( IllegalAccessException e ) {
			e.printStackTrace();
		}
	}

	@Override
	protected void
			doPost( HttpServletRequest request, HttpServletResponse response )	throws ServletException,
																				IOException {
		try {
			ActionForward action = processRequest( request,
					response,
					POST_METHOD,
					DEFAULT_MODULE );
			postRequest( request, response, action );
		}
		catch ( ClassNotFoundException e ) {
			e.printStackTrace();
		}
		catch ( InstantiationException e ) {
			e.printStackTrace();
		}
		catch ( IllegalAccessException e ) {
			e.printStackTrace();
		}
	}

	protected Collection<String> getRolesFromString( String roleString ) {
		Vector<String> roles = new Vector<String>();
		String subString;
		int index;
		roles = new Vector<String>();
		if ( roleString.indexOf( ',' ) == -1 ) {
			roles.add( roleString );
		} else {
			do {
				index = roleString.indexOf( ',' );
				if ( index != -1 ) {
					subString = roleString.substring( 0, index );
					roleString = roleString.substring( index + 1 );
					roles.add( subString );
				} else {
					roles.add( roleString );
					roleString = "";
				}
			}
			while ( roleString.length() > 0 );
		}
		return roles;
	}

	public KTMContext getAppContext() {
		return appContext;
	}

	protected boolean isEmptyString( String str ) {
		if ( str == null ) return true;
		if ( str.trim().isEmpty() ) return true;
		return false;
	}

	protected String getBasePath( HttpServletRequest request ) {
		return getServletContext( request ).getInitParameter( Globals.BASE_PATH );
	}

	protected void closeSession( HttpServletRequest request ) {
		request.setAttribute( Globals.ENTITY_SESSION_END_OF_CONVERSATION,
				Globals.ANY );
	}

}
